#include "Pch.h"

#include "Dx11FixedFunctionPipeline.h"
#include "Dx11Globals.h"
#include "Dx11Buffers.h"
#include "Dx11Shaders.h"

#include <memory>
#include "../../../MCD/Core/Math/Mat44.h"

namespace MCD
{
	std::string shader40(
	"struct VsIn\n"
	"{\n"
	"  float4 position  : POSITION;\n"
	"  float2 uv        : TEXCOORD0;\n"
	"  float4 color     : COLOR0;\n"
	"};\n"
	"struct VsOut\n"
	"{\n"
	"  float4 position  : SV_POSITION;\n"
	"  float2 uv        : TEXCOORD0;\n"
	"  float4 color     : COLOR0;\n"
	"};\n"
	"struct PsIn\n"
	"{\n"
	"  float4 position  : SV_POSITION;\n"
	"  float2 uv        : TEXCOORD0;\n"
	"  float4 color     : COLOR0;\n"
	"};\n"
	"struct PsOut\n"
	"{\n"
	"  float4 c0        : SV_TARGET0;\n"
	"};\n"
	"cbuffer ConstPerFrame : register( b0 )\n"
	"{\n"
    "  float4x4 g_WorldViewProjMtx : packoffset( c0 );\n"
	"};\n"
	"VsOut vsMain(VsIn _in)\n"
	"{\n"
	"  VsOut _out;\n"
	"  _out.position = mul(g_WorldViewProjMtx, _in.position);\n"
	"  _out.color = _in.color;\n"
	"  _out.uv = _in.uv;\n"
	"  return _out;\n"
	"}\n"
	"PsOut psMain(PsIn _in)\n"
	"{\n"
	"  PsOut _out;\n"
	"  _out.c0 = _in.color;\n"
	"  return _out;\n"
	"}\n"
	);
	class Dx11FixedFunctionPipeline::Impl
	{
	public:
		ID3D11VertexShader* mVertexShader;
		ID3D11PixelShader* mPixelShader;
		ID3D11Buffer* mVertexBuffer;
		ID3D11Buffer* mIndexBuffer;
		ID3D11InputLayout* mInputLayout;

		ID3D11DepthStencilState* mDepthStencilState;

		std::auto_ptr<Dx11ShaderConstants> mVSConst;
		std::auto_ptr<Dx11ShaderConstants> mPSConst;

		Impl()
		{
			mVertexBuffer = Dx11Buffers::createVertexDynamic(sizeof(Vertex) * 512, sizeof(Vertex));
			mIndexBuffer = Dx11Buffers::createIndexDynamic(sizeof(uint16_t) * 512, sizeof(uint32_t));

			ID3DBlob* byteCodeVS;
			ID3DBlob* byteCodePS;

			// vertex shader
			byteCodeVS = Dx11Shaders::compile(shader40.c_str(), shader40.size(), "", "vsMain", Dx11Shaders::getBestVertexShaderTarget());
			if(nullptr != byteCodeVS)
				mVertexShader = Dx11Shaders::createVertexShader(byteCodeVS);

			// pixel shader
			byteCodePS = Dx11Shaders::compile(shader40.c_str(), shader40.size(), "", "psMain", Dx11Shaders::getBestPixelShaderTarget());
			if(nullptr != byteCodePS)
				mPixelShader = Dx11Shaders::createPixelShader(byteCodePS);
			
			// input layout
			D3D11_INPUT_ELEMENT_DESC elems[] = {
				{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
				{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
				{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
			};

			mInputLayout = Dx11Buffers::createInputLayout(elems, 3, byteCodeVS);

			mVSConst.reset(new Dx11ShaderConstants(byteCodeVS));
			mPSConst.reset(new Dx11ShaderConstants(byteCodePS));
			
			safeRelease(byteCodeVS);
			safeRelease(byteCodePS);

			// depth stencil state
			{
				D3D11_DEPTH_STENCIL_DESC desc;
				memset(&desc, 0, sizeof(desc));
				desc.DepthEnable = TRUE;
				desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
				desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
				desc.StencilEnable = FALSE;

				getD3D11Device()->CreateDepthStencilState(&desc, &mDepthStencilState);
			}
		}

		~Impl()
		{
			safeRelease(mDepthStencilState);
			safeRelease(mInputLayout);
			safeRelease(mPixelShader);
			safeRelease(mVertexBuffer);
			safeRelease(mIndexBuffer);
			safeRelease(mVertexShader);
		}

		void begin()
		{
			MCD_ASSUME(mInputLayout);
			MCD_ASSUME(mVertexBuffer);
			MCD_ASSUME(mIndexBuffer);

			ID3D11DeviceContext* context = getD3D11DeviceContextIM();
			context->IASetInputLayout(mInputLayout);

			// vertex buffers
			{
				ID3D11Buffer* buffers[] = {mVertexBuffer};
				size_t strides[] = {sizeof(Vertex)};
				size_t offsets[] = {0};

				context->IASetVertexBuffers(0, 1, buffers, strides, offsets);
			}
			
			// index buffers
			{
				context->IASetIndexBuffer(mIndexBuffer, ::DXGI_FORMAT_R32_UINT, 0);
			}

			context->VSSetShader(mVertexShader, nullptr, 0);
			context->PSSetShader(mPixelShader, nullptr, 0);
			
			mVSConst->write("g_WorldViewProjMtx", (void*)&Mat44f::cIdentity, sizeof(Mat44f));
			
			if(mVSConst->count())
				mVSConst->bindVS();

			if(mPSConst->count())
				mPSConst->bindPS();

			context->OMSetDepthStencilState(mDepthStencilState, 0);
		}

		void end()
		{
		}

		void commitVertex(sal_notnull const Vertex* vertices, size_t vertexCnt)
		{
			ID3D11DeviceContext* context = getD3D11DeviceContextIM();

			D3D11_MAPPED_SUBRESOURCE subres;
			HRESULT hr = context->Map(mVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subres);

			if(FAILED(hr))
				return;

			memcpy(subres.pData, vertices, sizeof(Vertex) * vertexCnt);
			
			context->Unmap(mVertexBuffer, 0);
		}
	};

	Dx11FixedFunctionPipeline::Dx11FixedFunctionPipeline()
	{
		mImpl = new Impl();
	}

	Dx11FixedFunctionPipeline::~Dx11FixedFunctionPipeline()
	{
		delete mImpl;
	}

	void Dx11FixedFunctionPipeline::begin()
	{
		mImpl->begin();
	}

	void Dx11FixedFunctionPipeline::drawTriangleList(const Vertex* vertices, size_t vertexCnt)
	{
		mImpl->commitVertex(vertices, vertexCnt);

		ID3D11DeviceContext* context = getD3D11DeviceContextIM();
		context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		context->Draw(vertexCnt, 0);
	}

	void Dx11FixedFunctionPipeline::drawLineList(const Vertex* vertices, size_t vertexCnt)
	{
	}

	void Dx11FixedFunctionPipeline::drawPointList(const Vertex* vertices, size_t vertexCnt)
	{
	}

	void Dx11FixedFunctionPipeline::drawTriangleListIndexed(
		const Vertex* vertices, size_t vertexCnt,
		uint32_t* indices, size_t indexCnt
		)
	{
	}

	void Dx11FixedFunctionPipeline::drawLineListIndexed(
		const Vertex* vertices, size_t vertexCnt,
		uint32_t* indices, size_t indexCnt
		)
	{
	}

	void Dx11FixedFunctionPipeline::drawPointListIndexed(
		const Vertex* vertices, size_t vertexCnt,
		uint32_t* indices, size_t indexCnt
		)
	{
	}

	void Dx11FixedFunctionPipeline::end()
	{
		mImpl->end();
	}
}	// namespace MCD